Todo:
- add columns for totals for each pollutant/sector
# load libraries and read data
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
-- Attaching packages ----------------------------------------------------------------------------------------------------------- tidyverse 1.3.0 --
v ggplot2 3.3.2 v purrr 0.3.4
v tibble 3.1.0 v dplyr 1.0.2
v tidyr 1.1.2 v stringr 1.4.0
v readr 1.4.0 v forcats 0.5.0
package 㤼㸱tibble㤼㸲 was built under R version 4.0.4-- Conflicts -------------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag() masks stats::lag()
library(readxl)
library(plotly)
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5
Attaching package: 㤼㸱plotly㤼㸲
The following object is masked from 㤼㸱package:ggplot2㤼㸲:
last_plot
The following object is masked from 㤼㸱package:stats㤼㸲:
filter
The following object is masked from 㤼㸱package:graphics㤼㸲:
layout
ghg_emissions_clean <- read_csv("data/clean_data/ghg_emissions.csv")
-- Column specification ----------------------------------------------------------------------------------------------------------------------------
cols(
ccp_mapping = col_character(),
source_name = col_character(),
pollutant = col_character(),
year = col_double(),
value = col_double(),
units = col_character()
)
Only emissions - > emissions that are greater than 0
ghg_true_emissions <- ghg_emissions_clean %>%
filter(year == max(ghg_emissions_clean$year)) %>%
filter(value >= 0) %>%
mutate(across(where(is.character), ~str_to_title(.)))
# need to split by pollutant and year
ghg_emissions_clean %>%
select(ccp_mapping, source_name) %>%
filter(str_detect(source_name, paste("^", ccp_mapping, sep = ""))) %>%
unique()
# var_names
# get number of separators in source column
n_breaks <- max(str_count(ghg_emissions_clean$source_name, " - "), na.rm = TRUE)
# number of potential children = number of breaks + 1
# since: "one" - "two" - "three"
n_children <- n_breaks + 1
# create vector and fill with childnames
child_names <- c()
for (i in seq(1:n_children)) {
child_name <- paste("child_order_", i, sep = "")
child_names <- c(child_names, child_name)
}
child_names
[1] "child_order_1" "child_order_2" "child_order_3"
# clean source name column and split into child columns
ghg_wide <- ghg_emissions_clean %>%
mutate(source_name = str_to_title(
str_remove(
source_name,
paste("^", ccp_mapping, " - ", sep = "")
))) %>%
separate(source_name, into = child_names, sep = " - ", fill = "right") %>%
rename(child_order_0 = ccp_mapping)
ghg_wide_emissions <- ghg_wide %>%
filter(!value < 0)
ghg_wide_sinks <- ghg_wide %>%
filter(value < 0)
get_child_cols <- function(df, additional_vars, standard_vars = c("value", "units")) {
temp <- names(df)
remove <- c(additional_vars, standard_vars)
child_cols <- temp [!temp %in% remove]
}
child_cols <- get_child_cols(ghg_wide_emissions, additional_vars)
child_cols
[1] "child_order_0" "child_order_1" "child_order_2" "child_order_3"
previous_children
NULL
create_child_table(ghg_wide_emissions,
current_child = "child_order_0",
previous_children = NULL,
additional_vars = c("pollutant", "year"))
Error: Problem with `mutate()` input `..1`.
x Input `..1` must be a vector, not a `quosure/formula` object.
i Input `..1` is `current_child`.
Run `rlang::last_error()` to see where the error occurred.
previous_children <- NULL
children <- c(previous_children, "child_order_0")
children
[1] "child_order_0"
ghg_wide_emissions %>%
group_by(child_order_0, pollutant, year) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
# id must be created, parents are null - this is the top level
mutate(id = child_order_0, parent = "") %>%
select(id, label = child_order_0, parent, pollutant, year, value)
NA
ghg_wide_emissions %>%
group_by(child_order_2, child_order_1, child_order_0, pollutant, year) %>%
summarise(value = sum(value), .groups = "drop") %>%
mutate(id = paste(child_order_0, child_order_1, child_order_2, sep = " - "),
parent = paste(child_order_0, child_order_1, sep = " - ")) %>%
select(id, label = child_order_2, parent, pollutant, year, value)
ghg_wide_emissions %>%
group_by(child_order_3, child_order_2, child_order_1, child_order_0, pollutant, year) %>%
summarise(value = sum(value), .groups = "drop") %>%
mutate(id = paste(child_order_0, child_order_1, child_order_2, child_order_3, sep = " - "),
parent = paste(child_order_0, child_order_1, child_order_2, sep = " - ")) %>%
select(id, label = child_order_3, parent, pollutant, year, value)
create_child_table <- function(df, current_child, previous_children, additional_vars) {
current_child <- quo(current_child)
previous_children <- quo(previous_children)
additional_vars <- quo(additional_vars)
df %>%
group_by(!!current_child, rev(!!previous_children), !!additional_vars) %>%
summarise(value = sum(value), .groups = 'drop') %>%
unite(!!previous_children, col = "parent", sep = " - ") %>%
unite(c(!!previous_children, !!current_child), sep = " - ") %>%
select(id, label = !!current_child, parent, !!additional_vars, value)
}
names(ghg_wide_emissions) %in% "child_order_[0-9]"
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
temp <- names(ghg_wide_emissions)
remove <- c(additional_vars, "value", "units")
temp [!temp %in% remove]
[1] "child_order_0" "child_order_1" "child_order_2" "child_order_3"
TODO::
dashboard - review page dashboard - transport explorer
create_child_table(ghg_wide_emissions, current_child = "child_order_0",
previous_children = "", additional_vars = c("pollutant", "year"))
Error: Invalid index: out of bounds
create_child_table(ghg_wide_emissions, current_child = child_order_0,
previous_children = NULL, additional_vars = c(pollutant, year))
Error: Problem with `mutate()` input `..1`.
x Input `..1` must be a vector, not a `quosure/formula` object.
i Input `..1` is `current_child`.
Run `rlang::last_error()` to see where the error occurred.
if (is.null(previous_children)) {
print("bean")
}
[1] "bean"
vector <- c("child_1", "child_2")
paste(paste(vector, collapse = " - "),"child_3", sep = " - ")
[1] "child_1 - child_2 - child_3"
df %>% group_by(current_child, rev(previous_children), additional_vars) %>% summarise(value = sum(value), .groups = “drop”) %>% mutate(id = paste(previous_children, current_child, sep = " - “), parent = paste(previous_children, sep =” - ")) %>% select(id, label = current_child, parent, additional_vars, value)
vector <- c()
paste(paste(vector, sep = " - "),"child_3", sep = " - ")
[1] " - child_3"
vector <- c()
tibble(
parent = c("bean", "curd", "whey", "sprout", ""),
people = c("david", "sasha", "john", "smith", "delilah"),
shapes = c("triangle", "rectangle", "square", "diamond", "")
) %>%
unite(c(1,2,3), col = "id", sep = " - ", remove = FALSE)
tibble(
parent = c("dad", "bean", "", "fava"),
child = c("")
) %>%
select(child) %>%
pull()
[1] "" "" "" ""
# get sector totals for parent df (top level)
ghg_sector_totals <- ghg_true_emissions %>%
group_by(ccp_mapping) %>%
summarise(sector_total = sum(value), .groups = "drop_last")
parent_df <- ghg_sector_totals %>%
mutate(parent = "") %>%
select(label = ccp_mapping,
parent,
value = sector_total)
# the rest of the data
children_df <- ghg_true_emissions %>%
filter(year == max(ghg_true_emissions$year)) %>%
group_by(source_name, ccp_mapping) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
select(label = source_name,
parent = ccp_mapping,
value)
# combine into one hierarchical df, create id column
emissions_long <- bind_rows(list(parent_df, children_df)) %>%
mutate(id = paste(parent, label, sep = " - "), .before = 'label') %>%
mutate(id = str_remove(id, "^ - "))
Other method - seperate column
parents <- emissions_wide %>%
filter(is.na(second)) %>%
select(id, label, parent, value)
first_born <- emissions_wide %>%
filter(!is.na(second)) %>%
group_by(second, first) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
mutate(id = paste(first, second, sep = " - ")) %>%
ungroup() %>%
select(id,
label = second,
parent = first,
value)
second_born <- emissions_wide %>%
filter(!is.na(third)) %>%
group_by(third, second, first) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
mutate(id = paste(first, second, third, sep = " - ")) %>%
mutate(parent = paste(first, second, sep = " - ")) %>%
ungroup() %>%
select(id,
label = third,
parent,
value)
third_born <- emissions_wide %>%
filter(!is.na(fourth)) %>%
group_by(fourth, third, second, first) %>%
summarise(value = sum(value), .groups = "drop_last") %>%
mutate(id = paste(first, second, third, fourth, sep = " - ")) %>%
mutate(parent = paste(first, second, third, sep = " - ")) %>%
ungroup() %>%
select(id,
label = fourth,
parent,
value)
n_potential_child_layers <- emissions_long %>%
select(id) %>%
pull() %>%
str_count(" - ") %>%
max()
# the number of potential child layers is the maximum included in the dataset
n_potential_child_layers <- emissions_long %>%
select(id) %>%
pull() %>%
str_count(" - ") %>%
max()
create_hierarchy_df <- function(df, id_col = "id", id_sep = " - ") {
n_potential_child_layers <- df %>%
select(col) %>%
pull() %>%
str_count(sep) %>%
max()
}
emissions_long %>%
data.frame(stringsAsFactors = FALSE) %>%
plot_ly(
ids = ~id,
labels = ~label,
parents = ~parent,
values = ~value,
type = "sunburst",
maxdepth = 2,
insidetextorientation = 'radial',
marker=list(colorscale='Portland'),
text = ~units,
textinfo='label+percent root+percent entry',
hoverinfo = paste("%{label}: <br>%{value}",'text')
)
plot_ly(
labels = c("Eve", "Seth", "Enos", "Noam", "Awan", "Enoch"),
parents = c("", "Eve", "Seth", "Seth", "Eve", "Awan"),
values = c(16, 12, 10, 2, 4, 4),
type = "sunburst",
branchvalues = "total"
)
tibble(
labels = c("Eve", "Seth", "Enos", "Noam", "Awan", "Enoch"),
parents = c("", "Eve", "Seth", "Seth", "Eve", "Awan"),
values = c(16, 12, 10, 2, 4, 4)
)
fig <- plot_ly(
labels = cain_ble$labels,
parents = cain_ble$parents,
values = cain_ble$values,
type = 'sunburst'
)
fig
emissions_long$label[1:10]
[1] "Agriculture" "Electricity Generation" "Industry" "Land use" "Residential"
[6] "Services" "Transport" "Waste" "Accidental fires" "Accidental fires"
d <- data.frame(
ids = c(
"North America", "Europe", "Australia", "North America - Football", "Soccer",
"North America - Rugby", "Europe - Football", "Rugby",
"Europe - American Football","Australia - Football", "Association",
"Australian Rules", "Autstralia - American Football", "Australia - Rugby",
"Rugby League", "Rugby Union"
),
labels = c(
"North<br>America", "Europe", "Australia", "Football", "Soccer", "Rugby",
"Football", "Rugby", "American<br>Football", "Football", "Association",
"Australian<br>Rules", "American<br>Football", "Rugby", "Rugby<br>League",
"Rugby<br>Union"
),
parents = c(
"", "", "", "North America", "North America", "North America", "Europe",
"Europe", "Europe","Australia", "Australia - Football", "Australia - Football",
"Australia - Football", "Australia - Football", "Australia - Rugby",
"Australia - Rugby"
),
stringsAsFactors = FALSE
)
fig <- plot_ly(d, ids = ~ids, labels = ~labels, parents = ~parents, type = 'sunburst')
d
class(emissions_long)
[1] "tbl_df" "tbl" "data.frame"
class(cain_ble)
[1] "tbl_df" "tbl" "data.frame"
class(cain_ble)
diff_order <- ghg_emissions_clean %>%
dplyr::group_by(ccp_mapping, year, pollutant) %>%
dplyr::summarise(value = sum(value), units = units[1], .groups = "keep") %>%
filter(pollutant == "CO2") %>%
filter(year == min(ghg_emissions_clean$year) |
year == max(ghg_emissions_clean$year)) %>%
ungroup() %>%
group_by(ccp_mapping) %>%
mutate(diff = lag(value) - value) %>%
arrange(diff) %>%
drop_na() %>%
select(ccp_mapping) %>%
pull()
ghg_emissions_clean %>%
dplyr::group_by(ccp_mapping, year, pollutant) %>%
dplyr::summarise(value = sum(value), units = units[1], .groups = "keep") %>%
filter(pollutant == "CO2") %>%
mutate(ccp_mapping = factor(ccp_mapping, levels = rev(diff_order))) %>%
ggplot() +
aes(x = year, y = value, fill = ccp_mapping) +
geom_area() +
facet_wrap(~pollutant) +
theme_bw()
ghg_emissions_clean %>%
group_by(pollutant) %>%
summarise(emissions = sum(value))
plotly::ggplotly(
ghg_emissions_clean %>%
filter(year == 2018) %>%
filter(pollutant %in% c("CO2", "CH4")) %>%
ggplot() +
aes(x = factor(ccp_mapping, levels = rev(levels(factor(ccp_mapping)))),
y = value, fill = source_name,
text = paste0('</br> Sector: ', ccp_mapping,
'</br> Emissions: ', value,
'</br> Source Name: ', source_name)) +
geom_col(position = "stack") +
theme_bw() +
theme(legend.position = "none") +
labs(x = "Sector",
y = paste0("Emissions (", ghg_emissions_clean$units[1], ")")) +
coord_flip(),
tooltip = 'text'
)
ghg_emissions_data %>%
names()
ghg_emissions_data %>%
select()
input <- list()
input$col_choice = "national_communication_categories"
ghg_emissions_clean %>%
group_by_(input$col_choice, "emission_year") %>%
summarise(total_ghg_emissions = sum(emissions)) %>%
ggplot() +
aes(x = EmissionYear, y = total_ghg_emissions, group = `National Communication Categories`, colour = `National Communication Categories`) +
geom_line() +
geom_point() +
scale_x_continuous(breaks = seq(1990,2020,5)) +
theme(legend.position = 0)
ghg_emissions_data %>%
distinct(`National Communication Categories`)
ghg_emissions_data %>%
filter(EmissionYear != "BaseYear") %>%
mutate(EmissionYear = as.numeric(EmissionYear)) %>%
group_by(EmissionYear) %>%
summarise(total_ghg_emissions = sum(`Emissions (MtCO2e)`)) %>%
ggplot() +
aes(x = EmissionYear, y = total_ghg_emissions) +
geom_line() +
geom_point() +
scale_x_continuous(breaks = seq(1990,2020,5)) +
ylim(0, 80) +
theme(legend.position = 0) +
theme_bw()
ghg_emissions_data %>%
distinct(`CCP mapping`)
ghg_emissions_data %>%
filter(EmissionYear != "BaseYear") %>%
mutate(EmissionYear = as.numeric(EmissionYear)) %>%
group_by(`CCP mapping`, EmissionYear) %>%
summarise(total_ghg_emissions = sum(`Emissions (MtCO2e)`)) %>%
ggplot() +
aes(x = EmissionYear, y = total_ghg_emissions, group = `CCP mapping`, colour = `CCP mapping`) +
geom_line() +
geom_point() +
scale_x_continuous(breaks = seq(1990,2020,5))
`summarise()` regrouping output by 'CCP mapping' (override with `.groups` argument)

ghg_emissions_data %>%
filter(EmissionYear != "BaseYear") %>%
mutate(EmissionYear = as.numeric(EmissionYear)) %>%
filter(`National Communication Categories` != `CCP mapping`) %>%
select(`National Communication Categories`, `CCP mapping`) %>%
unique()
emissions_sankey <- emissions_data %>%
select(ccp_mapping, source_name, pollutant, emission_year, emissions, units)
category_id,category_name,subcategory_id,subcategory_name,year,emissions,emission
filtered_df <- ghg_emissions_clean %>%
select(ccp_mapping, source_name, pollutant, emission_year, emissions, units) %>%
filter(pollutant == "CO2") %>%
filter(emission_year == "2005")
total_emissions_for_gas <- filtered_df %>%
summarise(sum(emissions)) %>%
pull()
total_emissions_by_category <- filtered_df %>%
select(-pollutant) %>%
group_by(ccp_mapping) %>%
summarise(cat_sum = sum(emissions), .groups = 'drop_last')
categories <- filtered_df %>%
distinct(ccp_mapping) %>%
pull()
n_categories <- length(categories)
sources <- filtered_df %>%
distinct(source_name) %>%
pull()
n_sources <- length(sources)
n_sources
[1] 159
node_names <- c("Total", categories, sources, "Other")
node_names_df <- data.frame("name" = node_names)
total_sankey_tibble <- total_emissions_by_category %>%
mutate(total = "Total") %>%
mutate(total = match(total, node_names) -1) %>%
mutate(ccp_mapping = match(ccp_mapping, node_names) -1) %>%
select(source = total,
target = ccp_mapping,
value = cat_sum)
total_filtered_emissions <- total_sankey_tibble %>%
summarise(sum(value)) %>%
pull()
other_emissions <- total_emissions_for_gas - total_filtered_emissions
total_other_sankey_tibble <- tibble(
"source" = c(0),
"target" = (match("Other", node_names) -1),
"value" = c(other_emissions)
)
sub_sankey_tibble <- filtered_df %>%
select(- c(units, pollutant, emission_year)) %>%
mutate(ccp_mapping = match(ccp_mapping, node_names) -1,
source_name = match(source_name, node_names) -1)
names(sub_sankey_tibble) = c("source", "target", "value")
sankey_tibble <- total_sankey_tibble %>%
bind_rows(sub_sankey_tibble) %>%
bind_rows(total_other_sankey_tibble)
links_matrix <- data.frame(as.matrix(sankey_tibble, byrow = TRUE, ncols = 3))
# Add a 'group' column to each connection:
links <- links_matrix %>%
mutate(group = case_when(
source == 0 ~ paste("type_", target, sep = ""),
source!=0 ~ paste("type_", source, sep = "")
))
nodes <- node_names_df
# Add a 'group' column to each node.
# All of them in the same group to make them the same colour
nodes$group <- as.factor(c("my_unique_group"))
emissions <- list()
emissions$nodes <- nodes
emissions$links <- links
node_names <- c("Total", categories, sources, "Other")
node_names_df <- data.frame("name" = node_names)
total_sankey_tibble <- total_emissions_by_category %>%
mutate(total = "Total") %>%
mutate(total = match(total, node_names) -1) %>%
mutate(ccp_mapping = match(ccp_mapping, node_names) -1) %>%
select(source = total,
target = ccp_mapping,
value = cat_sum)
total_filtered_emissions <- total_sankey_tibble %>%
summarise(sum(value)) %>%
pull()
other_emissions <- total_emissions_for_gas - total_filtered_emissions
total_other_sankey_tibble <- tibble(
"source" = c(0),
"target" = (match("Other", node_names) -1),
"value" = c(other_emissions)
)
sub_sankey_tibble <- filtered_df %>%
select(- c(units, pollutant, emission_year)) %>%
mutate(ccp_mapping = match(ccp_mapping, node_names) -1,
source_name = match(source_name, node_names) -1)
names(sub_sankey_tibble) = c("source", "target", "value")
sankey_tibble <- total_sankey_tibble %>%
bind_rows(sub_sankey_tibble) %>%
bind_rows(total_other_sankey_tibble)
links_matrix <- data.frame(as.matrix(sankey_tibble, byrow = TRUE, ncols = 3))
# Add a 'group' column to each connection:
links <- links_matrix %>%
mutate(group = case_when(
source == 0 ~ paste("type_", target, sep = ""),
source!=0 ~ paste("type_", source, sep = "")
))
nodes <- node_names_df
# Add a 'group' column to each node.
# All of them in the same group to make them the same colour
nodes$group <- as.factor(c("my_unique_group"))
emissions <- list()
emissions$nodes <- nodes
emissions$links <- links
total_filtered_emissions <- total_sankey_tibble %>%
summarise(sum(value)) %>%
pull()
other_emissions <- total_emissions_for_gas - total_filtered_emissions
total_other_sankey_tibble <- tibble(
"source" = c(0),
"target" = (match("Other", node_names) -1),
"value" = c(other_emissions)
)
sub_sankey_tibble <- filtered_tibble %>%
select(-category_id, -subcategory_id, -year) %>%
mutate(category_name = match(category_name, node_names) -1,
subcategory_name = match(subcategory_name, node_names) -1)
names(sub_sankey_tibble) = c("source", "target", "value")
sankey_tibble <- total_sankey_tibble %>%
bind_rows(sub_sankey_tibble) %>%
bind_rows(total_other_sankey_tibble)
links_matrix <- data.frame(as.matrix(sankey_tibble, byrow = TRUE, ncols = 3))
# Add a 'group' column to each connection:
links <- links_matrix %>%
mutate(group = case_when(
source == 0 ~ paste("type_", target, sep = ""),
source!=0 ~ paste("type_", source, sep = "")
))
nodes <- node_names_df
# Add a 'group' column to each node.
# All of them in the same group to make them the same colour
nodes$group <- as.factor(c("my_unique_group"))
emissions <- list()
emissions$nodes <- nodes
emissions$links <- links
make_sankey_dfs <- function(data, userYear, userGas) {
n_categories <- data %>%
distinct(category_name) %>%
nrow()
total_emissions_for_gas <- data %>%
filter(emission == userGas()) %>%
filter(year == userYear()) %>%
summarise(sum(emissions)) %>%
pull()
filtered_tibble <- data %>%
filter(emission == userGas()) %>%
select(-emission) %>%
filter(year == userYear()) %>%
filter(emissions > userResolution())
total_emissions_by_cat <- filtered_tibble %>%
group_by(category_name) %>%
summarise(cat_sum = sum(emissions), .groups = 'drop_last')
categories <- filtered_tibble %>%
distinct(category_name) %>%
pull()
subcategories <- filtered_tibble %>%
distinct(subcategory_name) %>%
pull()
node_names <- c("Total", categories, subcategories, "Other")
node_names_df <- data.frame("name" = node_names)
total_sankey_tibble <- total_emissions_by_cat %>%
mutate(total = "Total") %>%
mutate(total = match(total, node_names) -1) %>%
mutate(category_name = match(category_name, node_names) -1) %>%
select(source = total,
target = category_name,
value = cat_sum)
total_filtered_emissions <- total_sankey_tibble %>%
summarise(sum(value)) %>%
pull()
other_emissions <- total_emissions_for_gas - total_filtered_emissions
total_other_sankey_tibble <- tibble(
"source" = c(0),
"target" = (match("Other", node_names) -1),
"value" = c(other_emissions)
)
sub_sankey_tibble <- filtered_tibble %>%
select(-category_id, -subcategory_id, -year) %>%
mutate(category_name = match(category_name, node_names) -1,
subcategory_name = match(subcategory_name, node_names) -1)
names(sub_sankey_tibble) = c("source", "target", "value")
sankey_tibble <- total_sankey_tibble %>%
bind_rows(sub_sankey_tibble) %>%
bind_rows(total_other_sankey_tibble)
links_matrix <- data.frame(as.matrix(sankey_tibble, byrow = TRUE, ncols = 3))
# Add a 'group' column to each connection:
links <- links_matrix %>%
mutate(group = case_when(
source == 0 ~ paste("type_", target, sep = ""),
source!=0 ~ paste("type_", source, sep = "")
))
nodes <- node_names_df
# Add a 'group' column to each node.
# All of them in the same group to make them the same colour
nodes$group <- as.factor(c("my_unique_group"))
emissions <- list()
emissions$nodes <- nodes
emissions$links <- links
return(emissions)
}
make_sankey_dfs(emissions_sankey, userYear = 2005, userGas = "CH4", userResolution = 50)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KIyBUb2RvOg0KDQotIGFkZCBjb2x1bW5zIGZvciB0b3RhbHMgZm9yIGVhY2ggcG9sbHV0YW50L3NlY3Rvcg0KDQpgYGB7cn0NCiMgbG9hZCBsaWJyYXJpZXMgYW5kIHJlYWQgZGF0YQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkocGxvdGx5KQ0KDQpnaGdfZW1pc3Npb25zX2NsZWFuIDwtIHJlYWRfY3N2KCJkYXRhL2NsZWFuX2RhdGEvZ2hnX2VtaXNzaW9ucy5jc3YiKQ0KYGBgDQoNCk9ubHkgZW1pc3Npb25zIC0gPiBlbWlzc2lvbnMgdGhhdCBhcmUgZ3JlYXRlciB0aGFuIDANCg0KYGBge3J9DQpnaGdfdHJ1ZV9lbWlzc2lvbnMgPC0gZ2hnX2VtaXNzaW9uc19jbGVhbiAlPiUgDQogIGZpbHRlcih5ZWFyID09IG1heChnaGdfZW1pc3Npb25zX2NsZWFuJHllYXIpKSAlPiUgDQogIGZpbHRlcih2YWx1ZSA+PSAwKSAlPiUgDQogIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfnN0cl90b190aXRsZSguKSkpDQpgYGANCg0KYGBge3J9DQojIG5lZWQgdG8gc3BsaXQgYnkgcG9sbHV0YW50IGFuZCB5ZWFyDQpnaGdfZW1pc3Npb25zX2NsZWFuICU+JSANCiAgc2VsZWN0KGNjcF9tYXBwaW5nLCBzb3VyY2VfbmFtZSkgJT4lIA0KICBmaWx0ZXIoc3RyX2RldGVjdChzb3VyY2VfbmFtZSwgcGFzdGUoIl4iLCBjY3BfbWFwcGluZywgc2VwID0gIiIpKSkgJT4lIA0KICB1bmlxdWUoKQ0KYGBgDQoNCmBgYHtyfQ0KIyB2YXJfbmFtZXMNCg0KIyBnZXQgbnVtYmVyIG9mIHNlcGFyYXRvcnMgaW4gc291cmNlIGNvbHVtbg0Kbl9icmVha3MgPC0gbWF4KHN0cl9jb3VudChnaGdfZW1pc3Npb25zX2NsZWFuJHNvdXJjZV9uYW1lLCAiIC0gIiksIG5hLnJtID0gVFJVRSkNCg0KIyBudW1iZXIgb2YgcG90ZW50aWFsIGNoaWxkcmVuICA9IG51bWJlciBvZiBicmVha3MgKyAxDQojIHNpbmNlOiAib25lIiAtICJ0d28iIC0gInRocmVlIg0Kbl9jaGlsZHJlbiA8LSBuX2JyZWFrcyArIDENCg0KIyBjcmVhdGUgdmVjdG9yIGFuZCBmaWxsIHdpdGggY2hpbGRuYW1lcw0KY2hpbGRfbmFtZXMgPC0gYygpDQoNCmZvciAoaSBpbiBzZXEoMTpuX2NoaWxkcmVuKSkgew0KICBjaGlsZF9uYW1lIDwtIHBhc3RlKCJjaGlsZF9vcmRlcl8iLCBpLCBzZXAgPSAiIikNCiAgY2hpbGRfbmFtZXMgPC0gYyhjaGlsZF9uYW1lcywgY2hpbGRfbmFtZSkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KIyBjbGVhbiBzb3VyY2UgbmFtZSBjb2x1bW4gYW5kIHNwbGl0IGludG8gY2hpbGQgY29sdW1ucw0KZ2hnX3dpZGUgPC0gZ2hnX2VtaXNzaW9uc19jbGVhbiAlPiUgDQogIG11dGF0ZShzb3VyY2VfbmFtZSA9IHN0cl90b190aXRsZSgNCiAgICBzdHJfcmVtb3ZlKA0KICAgICAgc291cmNlX25hbWUsDQogICAgICBwYXN0ZSgiXiIsIGNjcF9tYXBwaW5nLCAiIC0gIiwgc2VwID0gIiIpDQogICAgICApKSkgJT4lIA0KICBzZXBhcmF0ZShzb3VyY2VfbmFtZSwgaW50byA9IGNoaWxkX25hbWVzLCBzZXAgPSAiIC0gIiwgZmlsbCA9ICJyaWdodCIpICU+JSANCiAgcmVuYW1lKGNoaWxkX29yZGVyXzAgPSBjY3BfbWFwcGluZykNCmBgYA0KDQpgYGB7cn0NCmdoZ193aWRlX2VtaXNzaW9ucyA8LSBnaGdfd2lkZSAlPiUNCiAgZmlsdGVyKCF2YWx1ZSA8IDApDQoNCmdoZ193aWRlX3NpbmtzIDwtIGdoZ193aWRlICU+JSANCiAgZmlsdGVyKHZhbHVlIDwgMCkNCmBgYA0KDQpgYGB7cn0NCg0KZ2V0X2NoaWxkX2NvbHMgPC0gZnVuY3Rpb24oZGYsIGFkZGl0aW9uYWxfdmFycywgc3RhbmRhcmRfdmFycyA9IGMoInZhbHVlIiwgInVuaXRzIikpIHsNCiAgdGVtcCA8LSBuYW1lcyhkZikNCiAgcmVtb3ZlIDwtIGMoYWRkaXRpb25hbF92YXJzLCBzdGFuZGFyZF92YXJzKQ0KICANCiAgY2hpbGRfY29scyA8LSB0ZW1wIFshdGVtcCAlaW4lIHJlbW92ZV0NCn0NCg0KDQpjaGlsZF9jb2xzIDwtIGdldF9jaGlsZF9jb2xzKGdoZ193aWRlX2VtaXNzaW9ucywgYWRkaXRpb25hbF92YXJzKQ0KDQpjaGlsZF9jb2xzDQpgYGANCg0KYGBge3J9DQpjaGlsZF9jb2xzIDwtIGMoImNoaWxkX29yZGVyXzAiLCBjaGlsZF9uYW1lcykNCg0KcHJldmlvdXNfY2hpbGRyZW4gPC0gTlVMTA0KDQpoaWVyYXJjaHlfZGYgPC0gdGliYmxlKCkNCg0KZm9yIChjaGlsZCBpbiBjaGlsZF9jb2xzKSB7DQogIHByaW50KGNoaWxkKQ0KICBpbnRlcm1lZGlhdGVfdGliYmxlIDwtIGNyZWF0ZV9jaGlsZF90YWJsZShnaGdfd2lkZV9lbWlzc2lvbnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1cnJlbnRfY2hpbGQgPSBjaGlsZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJldmlvdXNfY2hpbGRyZW4gPSBwcmV2aW91c19jaGlsZHJlbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRkaXRpb25hbF92YXJzID0gYygicG9sbHV0YW50IiwgInllYXIiKSkNCiAgcHJldmlvdXNfY2hpbGRyZW4gPC0gYyhwcmV2aW91c19jaGlsZHJlbiwgY2hpbGQpDQogIGhpZXJhcmNoeV9kZiAlPiUgDQogICAgYmluZF9yb3dzKGludGVybWVkaWF0ZV90aWJibGUpDQp9DQoNCmhpZXJhcmNoeV9kZg0KYGBgDQoNCmBgYHtyfQ0KY3JlYXRlX2NoaWxkX3RhYmxlKGdoZ193aWRlX2VtaXNzaW9ucywNCiAgICAgICAgICAgICAgICAgICBjdXJyZW50X2NoaWxkID0gImNoaWxkX29yZGVyXzAiLA0KICAgICAgICAgICAgICAgICAgIHByZXZpb3VzX2NoaWxkcmVuID0gTlVMTCwNCiAgICAgICAgICAgICAgICAgICBhZGRpdGlvbmFsX3ZhcnMgPSBjKCJwb2xsdXRhbnQiLCAieWVhciIpKQ0KYGBgDQoNCg0KYGBge3J9DQpwcmV2aW91c19jaGlsZHJlbiA8LSBOVUxMDQoNCmNoaWxkcmVuIDwtIGMocHJldmlvdXNfY2hpbGRyZW4sICJjaGlsZF9vcmRlcl8wIikNCg0KY2hpbGRyZW4NCmBgYA0KDQoNCg0KYGBge3J9DQpnaGdfd2lkZV9lbWlzc2lvbnMgJT4lIA0KICBncm91cF9ieShjaGlsZF9vcmRlcl8wLCBwb2xsdXRhbnQsIHllYXIpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgLmdyb3VwcyA9ICJkcm9wX2xhc3QiKSAlPiUgDQogICMgaWQgbXVzdCBiZSBjcmVhdGVkLCBwYXJlbnRzIGFyZSBudWxsIC0gdGhpcyBpcyB0aGUgdG9wIGxldmVsDQogIG11dGF0ZShpZCA9IGNoaWxkX29yZGVyXzAsIHBhcmVudCA9ICIiKSAlPiUgDQogIHNlbGVjdChpZCwgbGFiZWwgPSBjaGlsZF9vcmRlcl8wLCBwYXJlbnQsIHBvbGx1dGFudCwgeWVhciwgdmFsdWUpDQoNCmBgYA0KDQpgYGB7cn0NCmdoZ193aWRlX2VtaXNzaW9ucyAlPiUNCiAgZ3JvdXBfYnkoY2hpbGRfb3JkZXJfMSwgY2hpbGRfb3JkZXJfMCwgcG9sbHV0YW50LCB5ZWFyKSAlPiUgDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSksIC5ncm91cHMgPSAiZHJvcCIpICU+JSANCiAgbXV0YXRlKGlkID0gcGFzdGUoY2hpbGRfb3JkZXJfMCwgY2hpbGRfb3JkZXJfMSwgc2VwID0gIiAtICIpLA0KICAgICAgICAgcGFyZW50ID0gY2hpbGRfb3JkZXJfMCkgJT4lIA0KICBzZWxlY3QoaWQsIGxhYmVsID0gY2hpbGRfb3JkZXJfMSwgcGFyZW50LCBwb2xsdXRhbnQsIHllYXIsIHZhbHVlKQ0KYGBgDQoNCmBgYHtyfQ0KZ2hnX3dpZGVfZW1pc3Npb25zICU+JQ0KICBncm91cF9ieShjaGlsZF9vcmRlcl8yLCBjaGlsZF9vcmRlcl8xLCBjaGlsZF9vcmRlcl8wLCBwb2xsdXRhbnQsIHllYXIpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lIA0KICBtdXRhdGUoaWQgPSBwYXN0ZShjaGlsZF9vcmRlcl8wLCBjaGlsZF9vcmRlcl8xLCBjaGlsZF9vcmRlcl8yLCBzZXAgPSAiIC0gIiksDQogICAgICAgICBwYXJlbnQgPSBwYXN0ZShjaGlsZF9vcmRlcl8wLCBjaGlsZF9vcmRlcl8xLCBzZXAgPSAiIC0gIikpICU+JSANCiAgc2VsZWN0KGlkLCBsYWJlbCA9IGNoaWxkX29yZGVyXzIsIHBhcmVudCwgcG9sbHV0YW50LCB5ZWFyLCB2YWx1ZSkNCmBgYA0KDQpgYGB7cn0NCmdoZ193aWRlX2VtaXNzaW9ucyAlPiUNCiAgZ3JvdXBfYnkoY2hpbGRfb3JkZXJfMywgY2hpbGRfb3JkZXJfMiwgY2hpbGRfb3JkZXJfMSwgY2hpbGRfb3JkZXJfMCwgcG9sbHV0YW50LCB5ZWFyKSAlPiUgDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSksIC5ncm91cHMgPSAiZHJvcCIpICU+JSANCiAgbXV0YXRlKGlkID0gcGFzdGUoY2hpbGRfb3JkZXJfMCwgY2hpbGRfb3JkZXJfMSwgY2hpbGRfb3JkZXJfMiwgY2hpbGRfb3JkZXJfMywgc2VwID0gIiAtICIpLA0KICAgICAgICAgcGFyZW50ID0gcGFzdGUoY2hpbGRfb3JkZXJfMCwgY2hpbGRfb3JkZXJfMSwgY2hpbGRfb3JkZXJfMiwgc2VwID0gIiAtICIpKSAlPiUgDQogIHNlbGVjdChpZCwgbGFiZWwgPSBjaGlsZF9vcmRlcl8zLCBwYXJlbnQsIHBvbGx1dGFudCwgeWVhciwgdmFsdWUpDQpgYGANCg0KYGBge3J9DQpjcmVhdGVfY2hpbGRfdGFibGUgPC0gZnVuY3Rpb24oZGYsIGN1cnJlbnRfY2hpbGQsIHByZXZpb3VzX2NoaWxkcmVuLCBhZGRpdGlvbmFsX3ZhcnMpIHsNCiAgaGllcmFyY2h5X2RmIDwtIGdoZ193aWRlX2VtaXNzaW9ucyAlPiUgDQogICAgZ3JvdXBfYnlfKC5kb3RzID0gYyhjdXJyZW50X2NoaWxkLA0KICAgICAgICAgICAgICAgICAgICAgICAgcmV2KHByZXZpb3VzX2NoaWxkcmVuKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGFkZGl0aW9uYWxfdmFycykpICU+JSANCiAgICBzdW1tYXJpc2UodmFsdWUgPSBzdW0odmFsdWUpLCAuZ3JvdXBzID0gJ2Ryb3AnKQ0KICANCiAgaWYgKGlzLm51bGwocHJldmlvdXNfY2hpbGRyZW4pKSB7DQogICAgaGllcmFyY2h5X2RmICU+JQ0KICAgICAgbXV0YXRlKHBhcmVudCA9ICIiKSAlPiUgDQogICAgICBzZWxlY3QoaWQgPSBjdXJyZW50X2NoaWxkLCBsYWJlbCA9IGN1cnJlbnRfY2hpbGQsDQogICAgICAgICAgICAgcGFyZW50LCBhZGRpdGlvbmFsX3ZhcnMsIHZhbHVlKQ0KICB9IGVsc2Ugew0KICAgIGhpZXJhcmNoeV9kZiAlPiUgDQogICAgICB1bml0ZShwcmV2aW91c19jaGlsZHJlbiwgY29sID0gInBhcmVudCIsIHNlcCA9ICIgLSAiLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICAgICAgdW5pdGUoYyhwcmV2aW91c19jaGlsZHJlbiwgY3VycmVudF9jaGlsZCksDQogICAgICAgICAgICBjb2wgPSAiaWQiLCBzZXAgPSAiIC0gIiwgcmVtb3ZlID0gRkFMU0UpICU+JSANCiAgICAgIHNlbGVjdChpZCwgbGFiZWwgPSBjdXJyZW50X2NoaWxkLCBwYXJlbnQsIGFkZGl0aW9uYWxfdmFycywgdmFsdWUpDQogIH0NCn0NCmBgYA0KDQoNCmBgYHtyfQ0KbmFtZXMoZ2hnX3dpZGVfZW1pc3Npb25zKSAlaW4lICJjaGlsZF9vcmRlcl9bMC05XSINCiAgDQp0ZW1wIDwtIG5hbWVzKGdoZ193aWRlX2VtaXNzaW9ucykNCnJlbW92ZSA8LSBjKGFkZGl0aW9uYWxfdmFycywgInZhbHVlIiwgInVuaXRzIikNCg0KDQp0ZW1wIFshdGVtcCAlaW4lIHJlbW92ZV0NCmBgYA0KDQpUT0RPOjoNCg0KZGFzaGJvYXJkIC0gcmV2aWV3IHBhZ2UNCmRhc2hib2FyZCAtIHRyYW5zcG9ydCBleHBsb3Jlcg0KDQoNCmBgYHtyfQ0KaGVscChtYWdyaXR0cikNCnNgYGANCg0KDQpgYGB7cn0NCmNyZWF0ZV9jaGlsZF90YWJsZShnaGdfd2lkZV9lbWlzc2lvbnMsIGN1cnJlbnRfY2hpbGQgPSAiY2hpbGRfb3JkZXJfMCIsDQogICAgICAgICAgICAgICAgICAgcHJldmlvdXNfY2hpbGRyZW4gPSAiIiwgYWRkaXRpb25hbF92YXJzID0gYygicG9sbHV0YW50IiwgInllYXIiKSkNCmBgYA0KDQoNCmBgYHtyfQ0KY3JlYXRlX2NoaWxkX3RhYmxlKGdoZ193aWRlX2VtaXNzaW9ucywgY3VycmVudF9jaGlsZCA9IGNoaWxkX29yZGVyXzAsDQogICAgICAgICAgICAgICAgICAgcHJldmlvdXNfY2hpbGRyZW4gPSBOVUxMLCBhZGRpdGlvbmFsX3ZhcnMgPSBjKHBvbGx1dGFudCwgeWVhcikpDQpgYGANCg0KYGBge3J9DQpjdXJyZW50X2NoaWxkIDwtICJjaGlsZF9vcmRlcl8xIg0KcHJldmlvdXNfY2hpbGRyZW4gPC0gImNoaWxkX29yZGVyXzAiDQphZGRpdGlvbmFsX3ZhcnMgPC0gYygicG9sbHV0YW50IiwgInllYXIiKQ0KDQpoaWVyYXJjaHlfZGYgPC0gZ2hnX3dpZGVfZW1pc3Npb25zICU+JSANCiAgZ3JvdXBfYnlfKC5kb3RzID0gYyhjdXJyZW50X2NoaWxkLCByZXYocHJldmlvdXNfY2hpbGRyZW4pLCBhZGRpdGlvbmFsX3ZhcnMpKSAlPiUgDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSksIC5ncm91cHMgPSAnZHJvcCcpDQoNCmlmIChpcy5udWxsKHByZXZpb3VzX2NoaWxkcmVuKSkgew0KICBoaWVyYXJjaHlfZGYgJT4lDQogICAgbXV0YXRlKHBhcmVudCA9ICIiKSAlPiUgDQogICAgc2VsZWN0KGlkID0gY3VycmVudF9jaGlsZCwgbGFiZWwgPSBjdXJyZW50X2NoaWxkLCBwYXJlbnQsIGFkZGl0aW9uYWxfdmFycywgdmFsdWUpDQp9IGVsc2Ugew0KICBoaWVyYXJjaHlfZGYgJT4lIA0KICAgIHVuaXRlKHByZXZpb3VzX2NoaWxkcmVuLCBjb2wgPSAicGFyZW50Iiwgc2VwID0gIiAtICIsIHJlbW92ZSA9IEZBTFNFKSAlPiUgDQogICAgdW5pdGUoYyhwcmV2aW91c19jaGlsZHJlbiwgY3VycmVudF9jaGlsZCksIGNvbCA9ICJpZCIsIHNlcCA9ICIgLSAiLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICAgIHNlbGVjdChpZCwgbGFiZWwgPSBjdXJyZW50X2NoaWxkLCBwYXJlbnQsIGFkZGl0aW9uYWxfdmFycywgdmFsdWUpDQp9DQogIA0KYGBgDQoNCmBgYHtyfQ0KaWYgKGlzLm51bGwocHJldmlvdXNfY2hpbGRyZW4pKSB7DQogIHByaW50KCJiZWFuIikNCn0NCmBgYA0KDQoNCg0KDQpkZiAlPiUNCiAgZ3JvdXBfYnkoY3VycmVudF9jaGlsZCwgcmV2KHByZXZpb3VzX2NoaWxkcmVuKSwgYWRkaXRpb25hbF92YXJzKSAlPiUgDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh2YWx1ZSksIC5ncm91cHMgPSAiZHJvcCIpICU+JSANCiAgbXV0YXRlKGlkID0gcGFzdGUocHJldmlvdXNfY2hpbGRyZW4sIGN1cnJlbnRfY2hpbGQsIHNlcCA9ICIgLSAiKSwNCiAgICAgICAgIHBhcmVudCA9IHBhc3RlKHByZXZpb3VzX2NoaWxkcmVuLCBzZXAgPSAiIC0gIikpICU+JSANCiAgc2VsZWN0KGlkLCBsYWJlbCA9IGN1cnJlbnRfY2hpbGQsIHBhcmVudCwgYWRkaXRpb25hbF92YXJzLCB2YWx1ZSkNCg0KYGBge3J9DQp2ZWN0b3IgPC0gYygiY2hpbGRfMSIsICJjaGlsZF8yIikNCg0KcGFzdGUocGFzdGUodmVjdG9yLCBjb2xsYXBzZSA9ICIgLSAiKSwiY2hpbGRfMyIsIHNlcCA9ICIgLSAiKQ0KYGBgDQoNCmBgYHtyfQ0KdmVjdG9yIDwtIGMoKQ0KDQpwYXN0ZShwYXN0ZSh2ZWN0b3IsIHNlcCA9ICIgLSAiKSwiY2hpbGRfMyIsIHNlcCA9ICIgLSAiKQ0KYGBgDQoNCg0KYGBge3J9DQp2ZWN0b3IgPC0gYygpDQoNCnRpYmJsZSgNCiAgcGFyZW50ID0gYygiYmVhbiIsICJjdXJkIiwgIndoZXkiLCAic3Byb3V0IiwgIiIpLA0KICBwZW9wbGUgPSBjKCJkYXZpZCIsICJzYXNoYSIsICJqb2huIiwgInNtaXRoIiwgImRlbGlsYWgiKSwNCiAgc2hhcGVzID0gYygidHJpYW5nbGUiLCAicmVjdGFuZ2xlIiwgInNxdWFyZSIsICJkaWFtb25kIiwgIiIpDQopICU+JSANCiAgdW5pdGUoYygxLDIsMyksIGNvbCA9ICJpZCIsIHNlcCA9ICIgLSAiLCByZW1vdmUgPSBGQUxTRSkNCmBgYA0KDQoNCmBgYHtyfQ0KdGliYmxlKA0KICBwYXJlbnQgPSBjKCJkYWQiLCAiYmVhbiIsICIiLCAiZmF2YSIpLA0KICBjaGlsZCA9IGMoIiIpDQopICU+JSANCiAgc2VsZWN0KGNoaWxkKSAlPiUgDQogIHB1bGwoKQ0KYGBgDQoNCg0KYGBge3J9DQojIGdldCBzZWN0b3IgdG90YWxzIGZvciBwYXJlbnQgZGYgKHRvcCBsZXZlbCkNCg0KZ2hnX3NlY3Rvcl90b3RhbHMgPC0gZ2hnX3RydWVfZW1pc3Npb25zICU+JSANCiAgZ3JvdXBfYnkoY2NwX21hcHBpbmcpICU+JSANCiAgc3VtbWFyaXNlKHNlY3Rvcl90b3RhbCA9IHN1bSh2YWx1ZSksIC5ncm91cHMgPSAiZHJvcF9sYXN0IikNCg0KcGFyZW50X2RmIDwtIGdoZ19zZWN0b3JfdG90YWxzICU+JSANCiAgbXV0YXRlKHBhcmVudCA9ICIiKSAlPiUgDQogIHNlbGVjdChsYWJlbCA9IGNjcF9tYXBwaW5nLA0KICAgICAgICAgcGFyZW50LA0KICAgICAgICAgdmFsdWUgPSBzZWN0b3JfdG90YWwpDQoNCiMgdGhlIHJlc3Qgb2YgdGhlIGRhdGENCg0KY2hpbGRyZW5fZGYgPC0gZ2hnX3RydWVfZW1pc3Npb25zICU+JSANCiAgZmlsdGVyKHllYXIgPT0gbWF4KGdoZ190cnVlX2VtaXNzaW9ucyR5ZWFyKSkgJT4lIA0KICBncm91cF9ieShzb3VyY2VfbmFtZSwgY2NwX21hcHBpbmcpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgLmdyb3VwcyA9ICJkcm9wX2xhc3QiKSAlPiUgDQogIHNlbGVjdChsYWJlbCA9IHNvdXJjZV9uYW1lLA0KICAgICAgICAgcGFyZW50ID0gY2NwX21hcHBpbmcsDQogICAgICAgICB2YWx1ZSkNCg0KIyBjb21iaW5lIGludG8gb25lIGhpZXJhcmNoaWNhbCBkZiwgY3JlYXRlIGlkIGNvbHVtbg0KDQplbWlzc2lvbnNfbG9uZyA8LSBiaW5kX3Jvd3MobGlzdChwYXJlbnRfZGYsIGNoaWxkcmVuX2RmKSkgJT4lIA0KICBtdXRhdGUoaWQgPSBwYXN0ZShwYXJlbnQsIGxhYmVsLCBzZXAgPSAiIC0gIiksIC5iZWZvcmUgPSAnbGFiZWwnKSAlPiUgDQogIG11dGF0ZShpZCA9IHN0cl9yZW1vdmUoaWQsICJeIC0gIikpDQpgYGANCg0KIyBPdGhlciBtZXRob2QgLSBzZXBlcmF0ZSBjb2x1bW4NCg0KYGBge3J9DQplbWlzc2lvbnNfd2lkZSA8LSBlbWlzc2lvbnNfbG9uZyAlPiUgDQogIHNlcGFyYXRlKGlkLCAiIC0gIiwgaW50byA9IGMoImZpcnN0IiwgInNlY29uZCIsICJ0aGlyZCIsICJmb3VydGgiKSwNCiAgICAgICAgICAgcmVtb3ZlID0gRkFMU0UsIGV4dHJhID0gIm1lcmdlIiwgZmlsbCA9ICJyaWdodCIpDQpgYGANCg0KYGBge3J9DQpwYXJlbnRzIDwtIGVtaXNzaW9uc193aWRlICU+JSANCiAgZmlsdGVyKGlzLm5hKHNlY29uZCkpICU+JSANCiAgc2VsZWN0KGlkLCBsYWJlbCwgcGFyZW50LCB2YWx1ZSkNCmBgYA0KDQpgYGB7cn0NCmZpcnN0X2Jvcm4gPC0gZW1pc3Npb25zX3dpZGUgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKHNlY29uZCkpICU+JSANCiAgZ3JvdXBfYnkoc2Vjb25kLCBmaXJzdCkgJT4lIA0KICBzdW1tYXJpc2UodmFsdWUgPSBzdW0odmFsdWUpLCAuZ3JvdXBzID0gImRyb3BfbGFzdCIpICU+JSANCiAgbXV0YXRlKGlkID0gcGFzdGUoZmlyc3QsIHNlY29uZCwgc2VwID0gIiAtICIpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIHNlbGVjdChpZCwNCiAgICAgICAgIGxhYmVsID0gc2Vjb25kLA0KICAgICAgICAgcGFyZW50ID0gZmlyc3QsDQogICAgICAgICB2YWx1ZSkNCmBgYA0KDQpgYGB7cn0NCnNlY29uZF9ib3JuIDwtIGVtaXNzaW9uc193aWRlICU+JSANCiAgZmlsdGVyKCFpcy5uYSh0aGlyZCkpICU+JSANCiAgZ3JvdXBfYnkodGhpcmQsIHNlY29uZCwgZmlyc3QpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgLmdyb3VwcyA9ICJkcm9wX2xhc3QiKSAlPiUgDQogIG11dGF0ZShpZCA9IHBhc3RlKGZpcnN0LCBzZWNvbmQsIHRoaXJkLCBzZXAgPSAiIC0gIikpICU+JSANCiAgbXV0YXRlKHBhcmVudCA9IHBhc3RlKGZpcnN0LCBzZWNvbmQsIHNlcCA9ICIgLSAiKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBzZWxlY3QoaWQsDQogICAgICAgICBsYWJlbCA9IHRoaXJkLA0KICAgICAgICAgcGFyZW50LA0KICAgICAgICAgdmFsdWUpDQpgYGANCg0KYGBge3J9DQp0aGlyZF9ib3JuIDwtIGVtaXNzaW9uc193aWRlICU+JSANCiAgZmlsdGVyKCFpcy5uYShmb3VydGgpKSAlPiUgDQogIGdyb3VwX2J5KGZvdXJ0aCwgdGhpcmQsIHNlY29uZCwgZmlyc3QpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgLmdyb3VwcyA9ICJkcm9wX2xhc3QiKSAlPiUgDQogIG11dGF0ZShpZCA9IHBhc3RlKGZpcnN0LCBzZWNvbmQsIHRoaXJkLCBmb3VydGgsIHNlcCA9ICIgLSAiKSkgJT4lIA0KICBtdXRhdGUocGFyZW50ID0gcGFzdGUoZmlyc3QsIHNlY29uZCwgdGhpcmQsIHNlcCA9ICIgLSAiKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBzZWxlY3QoaWQsDQogICAgICAgICBsYWJlbCA9IGZvdXJ0aCwNCiAgICAgICAgIHBhcmVudCwNCiAgICAgICAgIHZhbHVlKQ0KYGBgDQoNCmBgYHtyfQ0KIyB0aGUgbnVtYmVyIG9mIHBvdGVudGlhbCBjaGlsZCBsYXllcnMgaXMgdGhlIG1heGltdW0gaW5jbHVkZWQgaW4gdGhlIGRhdGFzZXQNCm5fcG90ZW50aWFsX2NoaWxkX2xheWVycyA8LSBlbWlzc2lvbnNfbG9uZyAlPiUgDQogIHNlbGVjdChpZCkgJT4lIA0KICBwdWxsKCkgJT4lIA0KICBzdHJfY291bnQoIiAtICIpICU+JSANCiAgbWF4KCkNCmBgYA0KDQoNCg0KYGBge3J9DQpjcmVhdGVfaGllcmFyY2h5X2RmIDwtIGZ1bmN0aW9uKGRmLCBpZF9jb2wgPSAiaWQiLCBpZF9zZXAgPSAiIC0gIikgew0KICBuX3BvdGVudGlhbF9jaGlsZF9sYXllcnMgPC0gZGYgJT4lIA0KICAgIHNlbGVjdChjb2wpICU+JSANCiAgICBwdWxsKCkgJT4lIA0KICAgIHN0cl9jb3VudChzZXApICU+JSANCiAgICBtYXgoKQ0KICANCiAgDQp9DQpgYGANCg0KDQoNCmBgYHtyfQ0KYGBgDQoNCg0KYGBge3J9DQplbWlzc2lvbnNfbG9uZyA8LSBiaW5kX3Jvd3MobGlzdChwYXJlbnRzLCBmaXJzdF9ib3JuLCBzZWNvbmRfYm9ybiwgdGhpcmRfYm9ybikpICU+JSANCiAgbXV0YXRlKHVuaXRzID0gZ2hnX2VtaXNzaW9uc19jbGVhbiR1bml0c1sxXSkgJT4lIA0KICB3cml0ZV9jc3YoImRhdGEvY2xlYW5fZGF0YS9oaWVyYXJjaGljYWxfZGF0YS5jc3YiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmVtaXNzaW9uc19sb25nICU+JQ0KICBkYXRhLmZyYW1lKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgJT4lIA0KICBwbG90X2x5KA0KICAgIGlkcyA9IH5pZCwNCiAgICBsYWJlbHMgPSB+bGFiZWwsDQogICAgcGFyZW50cyA9IH5wYXJlbnQsDQogICAgdmFsdWVzID0gfnZhbHVlLA0KICAgIHR5cGUgPSAidHJlZW1hcCIsDQogICAgYnJhbmNodmFsdWVzID0gInRvdGFsIiwNCiAgICBtYXhkZXB0aCA9IDIsDQogICAgdGV4dGluZm89J2xhYmVsK3BlcmNlbnQgcm9vdCtlbnRyeScNCiAgKQ0KYGBgDQoNCg0KYGBge3J9DQplbWlzc2lvbnNfbG9uZyAlPiUgDQogIGRhdGEuZnJhbWUoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUgDQogIHBsb3RfbHkoDQogICAgaWRzID0gfmlkLA0KICAgIGxhYmVscyA9IH5sYWJlbCwNCiAgICBwYXJlbnRzID0gfnBhcmVudCwNCiAgICB2YWx1ZXMgPSB+dmFsdWUsDQogICAgdHlwZSA9ICJzdW5idXJzdCIsDQogICAgbWF4ZGVwdGggPSAyLA0KICAgIGluc2lkZXRleHRvcmllbnRhdGlvbiA9ICdyYWRpYWwnLA0KICAgIG1hcmtlcj1saXN0KGNvbG9yc2NhbGU9J1BvcnRsYW5kJyksDQogICAgdGV4dCA9IH51bml0cywNCiAgICB0ZXh0aW5mbz0nbGFiZWwrcGVyY2VudCByb290K3BlcmNlbnQgZW50cnknLA0KICAgIGhvdmVyaW5mbyA9IHBhc3RlKCIle2xhYmVsfTogPGJyPiV7dmFsdWV9IiwndGV4dCcpDQogICkNCmBgYA0KDQoNCg0KYGBge3J9DQpwbG90X2x5KA0KICBsYWJlbHMgPSBjKCJFdmUiLCAiU2V0aCIsICJFbm9zIiwgIk5vYW0iLCAiQXdhbiIsICJFbm9jaCIpLA0KICBwYXJlbnRzID0gYygiIiwgIkV2ZSIsICJTZXRoIiwgIlNldGgiLCAiRXZlIiwgIkF3YW4iKSwNCiAgdmFsdWVzID0gYygxNiwgMTIsIDEwLCAyLCA0LCA0KSwNCiAgdHlwZSA9ICJzdW5idXJzdCIsDQogIGJyYW5jaHZhbHVlcyA9ICJ0b3RhbCINCikNCmBgYA0KDQpgYGB7cn0NCnRpYmJsZSgNCiAgbGFiZWxzID0gYygiRXZlIiwgIlNldGgiLCAiRW5vcyIsICJOb2FtIiwgIkF3YW4iLCAiRW5vY2giKSwNCiAgcGFyZW50cyA9IGMoIiIsICJFdmUiLCAiU2V0aCIsICJTZXRoIiwgIkV2ZSIsICJBd2FuIiksDQogIHZhbHVlcyA9IGMoMTYsIDEyLCAxMCwgMiwgNCwgNCkNCikNCmBgYA0KDQpgYGB7cn0NCmVtaXNzaW9uc19sb25nDQpgYGANCg0KDQpgYGB7cn0NCmZpZyA8LSBwbG90X2x5KA0KICBsYWJlbHMgPSBjYWluX2JsZSRsYWJlbHMsDQogIHBhcmVudHMgPSBjYWluX2JsZSRwYXJlbnRzLA0KICB2YWx1ZXMgPSBjYWluX2JsZSR2YWx1ZXMsDQogIHR5cGUgPSAnc3VuYnVyc3QnDQopDQoNCmZpZw0KYGBgDQoNCg0KYGBge3J9DQplbWlzc2lvbnNfbG9uZyRsYWJlbFsxOjEwXQ0KYGBgDQoNCg0KYGBge3J9DQpkIDwtIGRhdGEuZnJhbWUoDQogICAgaWRzID0gYygNCiAgICAiTm9ydGggQW1lcmljYSIsICJFdXJvcGUiLCAiQXVzdHJhbGlhIiwgIk5vcnRoIEFtZXJpY2EgLSBGb290YmFsbCIsICJTb2NjZXIiLA0KICAgICJOb3J0aCBBbWVyaWNhIC0gUnVnYnkiLCAiRXVyb3BlIC0gRm9vdGJhbGwiLCAiUnVnYnkiLA0KICAgICJFdXJvcGUgLSBBbWVyaWNhbiBGb290YmFsbCIsIkF1c3RyYWxpYSAtIEZvb3RiYWxsIiwgIkFzc29jaWF0aW9uIiwNCiAgICAiQXVzdHJhbGlhbiBSdWxlcyIsICJBdXRzdHJhbGlhIC0gQW1lcmljYW4gRm9vdGJhbGwiLCAiQXVzdHJhbGlhIC0gUnVnYnkiLA0KICAgICJSdWdieSBMZWFndWUiLCAiUnVnYnkgVW5pb24iDQogICksDQogIGxhYmVscyA9IGMoDQogICAgIk5vcnRoPGJyPkFtZXJpY2EiLCAiRXVyb3BlIiwgIkF1c3RyYWxpYSIsICJGb290YmFsbCIsICJTb2NjZXIiLCAiUnVnYnkiLA0KICAgICJGb290YmFsbCIsICJSdWdieSIsICJBbWVyaWNhbjxicj5Gb290YmFsbCIsICJGb290YmFsbCIsICJBc3NvY2lhdGlvbiIsDQogICAgIkF1c3RyYWxpYW48YnI+UnVsZXMiLCAiQW1lcmljYW48YnI+Rm9vdGJhbGwiLCAiUnVnYnkiLCAiUnVnYnk8YnI+TGVhZ3VlIiwNCiAgICAiUnVnYnk8YnI+VW5pb24iDQogICksDQogIHBhcmVudHMgPSBjKA0KICAgICIiLCAiIiwgIiIsICJOb3J0aCBBbWVyaWNhIiwgIk5vcnRoIEFtZXJpY2EiLCAiTm9ydGggQW1lcmljYSIsICJFdXJvcGUiLA0KICAgICJFdXJvcGUiLCAiRXVyb3BlIiwiQXVzdHJhbGlhIiwgIkF1c3RyYWxpYSAtIEZvb3RiYWxsIiwgIkF1c3RyYWxpYSAtIEZvb3RiYWxsIiwNCiAgICAiQXVzdHJhbGlhIC0gRm9vdGJhbGwiLCAiQXVzdHJhbGlhIC0gRm9vdGJhbGwiLCAiQXVzdHJhbGlhIC0gUnVnYnkiLA0KICAgICJBdXN0cmFsaWEgLSBSdWdieSINCiAgKSwNCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFDQopDQoNCmZpZyA8LSBwbG90X2x5KGQsIGlkcyA9IH5pZHMsIGxhYmVscyA9IH5sYWJlbHMsIHBhcmVudHMgPSB+cGFyZW50cywgdHlwZSA9ICdzdW5idXJzdCcpDQoNCmQNCmBgYA0KDQpgYGB7cn0NCmNsYXNzKGVtaXNzaW9uc19sb25nKQ0KYGBgDQpgYGB7cn0NCmNsYXNzKGNhaW5fYmxlKQ0KYGBgDQoNCg0KYGBge3J9DQpkaWZmX29yZGVyIDwtIGdoZ19lbWlzc2lvbnNfY2xlYW4gJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoY2NwX21hcHBpbmcsIHllYXIsIHBvbGx1dGFudCkgJT4lIA0KICBkcGx5cjo6c3VtbWFyaXNlKHZhbHVlID0gc3VtKHZhbHVlKSwgdW5pdHMgPSB1bml0c1sxXSwgLmdyb3VwcyA9ICJrZWVwIikgJT4lDQogIGZpbHRlcihwb2xsdXRhbnQgPT0gIkNPMiIpICU+JSANCiAgZmlsdGVyKHllYXIgPT0gbWluKGdoZ19lbWlzc2lvbnNfY2xlYW4keWVhcikgfA0KICAgICAgICAgICB5ZWFyID09IG1heChnaGdfZW1pc3Npb25zX2NsZWFuJHllYXIpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGdyb3VwX2J5KGNjcF9tYXBwaW5nKSAlPiUgDQogIG11dGF0ZShkaWZmID0gbGFnKHZhbHVlKSAtIHZhbHVlKSAlPiUgDQogIGFycmFuZ2UoZGlmZikgJT4lDQogIGRyb3BfbmEoKSAlPiUgDQogIHNlbGVjdChjY3BfbWFwcGluZykgJT4lIA0KICBwdWxsKCkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2hnX2VtaXNzaW9uc19jbGVhbiAlPiUgDQogIGRwbHlyOjpncm91cF9ieShjY3BfbWFwcGluZywgeWVhciwgcG9sbHV0YW50KSAlPiUgDQogIGRwbHlyOjpzdW1tYXJpc2UodmFsdWUgPSBzdW0odmFsdWUpLCB1bml0cyA9IHVuaXRzWzFdLCAuZ3JvdXBzID0gImtlZXAiKSAlPiUNCiAgZmlsdGVyKHBvbGx1dGFudCA9PSAiQ08yIikgJT4lDQogIG11dGF0ZShjY3BfbWFwcGluZyA9IGZhY3RvcihjY3BfbWFwcGluZywgbGV2ZWxzID0gcmV2KGRpZmZfb3JkZXIpKSkgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGFlcyh4ID0geWVhciwgeSA9IHZhbHVlLCBmaWxsID0gY2NwX21hcHBpbmcpICsNCiAgZ2VvbV9hcmVhKCkgKw0KICBmYWNldF93cmFwKH5wb2xsdXRhbnQpICsNCiAgdGhlbWVfYncoKQ0KYGBgDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfY2xlYW4gJT4lIA0KICBncm91cF9ieShwb2xsdXRhbnQpICU+JSANCiAgc3VtbWFyaXNlKGVtaXNzaW9ucyA9IHN1bSh2YWx1ZSkpDQpgYGANCg0KDQpgYGB7cn0NCnBsb3RseTo6Z2dwbG90bHkoDQogIGdoZ19lbWlzc2lvbnNfY2xlYW4gJT4lIA0KICAgIGZpbHRlcih5ZWFyID09IDIwMTgpICU+JQ0KICAgIGZpbHRlcihwb2xsdXRhbnQgJWluJSBjKCJDTzIiLCAiQ0g0IikpICU+JQ0KICAgIGdncGxvdCgpICsNCiAgICBhZXMoeCA9IGZhY3RvcihjY3BfbWFwcGluZywgbGV2ZWxzID0gcmV2KGxldmVscyhmYWN0b3IoY2NwX21hcHBpbmcpKSkpLA0KICAgICAgICB5ID0gdmFsdWUsIGZpbGwgPSBzb3VyY2VfbmFtZSwNCiAgICAgICAgdGV4dCA9IHBhc3RlMCgnPC9icj4gU2VjdG9yOiAnLCBjY3BfbWFwcGluZywNCiAgICAgICAgICAgICAgICAgICAgICAnPC9icj4gRW1pc3Npb25zOiAnLCB2YWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgICAnPC9icj4gU291cmNlIE5hbWU6ICcsIHNvdXJjZV9uYW1lKSkgKw0KICAgIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIikgKw0KICAgIHRoZW1lX2J3KCkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICAgIGxhYnMoeCA9ICJTZWN0b3IiLA0KICAgICAgICAgeSA9IHBhc3RlMCgiRW1pc3Npb25zICgiLCBnaGdfZW1pc3Npb25zX2NsZWFuJHVuaXRzWzFdLCAiKSIpKSArDQogICAgY29vcmRfZmxpcCgpLA0KICAgIHRvb2x0aXAgPSAndGV4dCcNCiAgKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfZGF0YSAlPiUgDQogIG5hbWVzKCkNCmBgYA0KDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfZGF0YSAlPiUgDQogIHNlbGVjdCgpDQpgYGANCg0KDQoNCmBgYHtyfQ0KaW5wdXQgPC0gbGlzdCgpDQoNCmlucHV0JGNvbF9jaG9pY2UgPSAibmF0aW9uYWxfY29tbXVuaWNhdGlvbl9jYXRlZ29yaWVzIg0KYGBgDQoNCg0KDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfY2xlYW4gJT4lDQogIGdyb3VwX2J5XyhpbnB1dCRjb2xfY2hvaWNlLCAiZW1pc3Npb25feWVhciIpICU+JSANCiAgc3VtbWFyaXNlKHRvdGFsX2doZ19lbWlzc2lvbnMgPSBzdW0oZW1pc3Npb25zKSkgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGFlcyh4ID0gRW1pc3Npb25ZZWFyLCB5ID0gdG90YWxfZ2hnX2VtaXNzaW9ucywgZ3JvdXAgPSBgTmF0aW9uYWwgQ29tbXVuaWNhdGlvbiBDYXRlZ29yaWVzYCwgY29sb3VyID0gYE5hdGlvbmFsIENvbW11bmljYXRpb24gQ2F0ZWdvcmllc2ApICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDE5OTAsMjAyMCw1KSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAwKQ0KYGBgDQoNCmBgYHtyfQ0KZ2hnX2VtaXNzaW9uc19kYXRhICU+JSANCiAgZGlzdGluY3QoYE5hdGlvbmFsIENvbW11bmljYXRpb24gQ2F0ZWdvcmllc2ApDQpgYGANCmBgYHtyfQ0KZ2hnX2VtaXNzaW9uc19kYXRhICU+JSANCiAgZmlsdGVyKEVtaXNzaW9uWWVhciAhPSAiQmFzZVllYXIiKSAlPiUgDQogIG11dGF0ZShFbWlzc2lvblllYXIgPSBhcy5udW1lcmljKEVtaXNzaW9uWWVhcikpICU+JSANCiAgZ3JvdXBfYnkoRW1pc3Npb25ZZWFyKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbF9naGdfZW1pc3Npb25zID0gc3VtKGBFbWlzc2lvbnMgKE10Q08yZSlgKSkgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGFlcyh4ID0gRW1pc3Npb25ZZWFyLCB5ID0gdG90YWxfZ2hnX2VtaXNzaW9ucykgKw0KICBnZW9tX2xpbmUoKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMTk5MCwyMDIwLDUpKSArDQogIHlsaW0oMCwgODApICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gMCkgKw0KICB0aGVtZV9idygpDQpgYGANCg0KDQpgYGB7cn0NCmdoZ19lbWlzc2lvbnNfZGF0YSAlPiUgDQogIGRpc3RpbmN0KGBDQ1AgbWFwcGluZ2ApDQpgYGANCg0KYGBge3J9DQoNCmBgYA0KDQoNCmBgYHtyfQ0KZ2hnX2VtaXNzaW9uc19kYXRhICU+JSANCiAgZGlzdGluY3QoYE5hdGlvbmFsIENvbW11bmljYXRpb24gQ2F0ZWdvcmllc2ApDQpgYGANCmBgYHtyfQ0KZ2hnX2VtaXNzaW9uc19kYXRhDQpgYGANCmBgYHtyfQ0KZ2hnX2VtaXNzaW9uc19kYXRhICU+JSANCiAgZmlsdGVyKEVtaXNzaW9uWWVhciAhPSAiQmFzZVllYXIiKSAlPiUgDQogIG11dGF0ZShFbWlzc2lvblllYXIgPSBhcy5udW1lcmljKEVtaXNzaW9uWWVhcikpICU+JSANCiAgZ3JvdXBfYnkoYENDUCBtYXBwaW5nYCwgRW1pc3Npb25ZZWFyKSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbF9naGdfZW1pc3Npb25zID0gc3VtKGBFbWlzc2lvbnMgKE10Q08yZSlgKSkgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGFlcyh4ID0gRW1pc3Npb25ZZWFyLCB5ID0gdG90YWxfZ2hnX2VtaXNzaW9ucywgZ3JvdXAgPSBgQ0NQIG1hcHBpbmdgLCBjb2xvdXIgPSBgQ0NQIG1hcHBpbmdgKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxOTkwLDIwMjAsNSkpDQpgYGANCg0KYGBge3J9DQpnaGdfZW1pc3Npb25zX2RhdGEgJT4lIA0KICBmaWx0ZXIoRW1pc3Npb25ZZWFyICE9ICJCYXNlWWVhciIpICU+JSANCiAgbXV0YXRlKEVtaXNzaW9uWWVhciA9IGFzLm51bWVyaWMoRW1pc3Npb25ZZWFyKSkgJT4lIA0KICBmaWx0ZXIoYE5hdGlvbmFsIENvbW11bmljYXRpb24gQ2F0ZWdvcmllc2AgIT0gYENDUCBtYXBwaW5nYCkgJT4lIA0KICBzZWxlY3QoYE5hdGlvbmFsIENvbW11bmljYXRpb24gQ2F0ZWdvcmllc2AsIGBDQ1AgbWFwcGluZ2ApICU+JSANCiAgdW5pcXVlKCkNCmBgYA0KY2F0ZWdvcnlfaWQsY2F0ZWdvcnlfbmFtZSxzdWJjYXRlZ29yeV9pZCxzdWJjYXRlZ29yeV9uYW1lLHllYXIsZW1pc3Npb25zLGVtaXNzaW9uDQoNCmBgYHtyfQ0KZW1pc3Npb25zX3NhbmtleSA8LSBlbWlzc2lvbnNfZGF0YSAlPiUgDQogIHNlbGVjdChjY3BfbWFwcGluZywgc291cmNlX25hbWUsIHBvbGx1dGFudCwgZW1pc3Npb25feWVhciwgZW1pc3Npb25zLCB1bml0cykNCmBgYA0KDQoNCmBgYHtyfQ0KZ2hnX2VtaXNzaW9uc19jbGVhbg0KYGBgDQoNCg0KYGBge3J9DQpmaWx0ZXJlZF9kZiA8LSBnaGdfZW1pc3Npb25zX2NsZWFuICU+JSANCiAgc2VsZWN0KGNjcF9tYXBwaW5nLCBzb3VyY2VfbmFtZSwgcG9sbHV0YW50LCBlbWlzc2lvbl95ZWFyLCBlbWlzc2lvbnMsIHVuaXRzKSAlPiUgDQogIGZpbHRlcihwb2xsdXRhbnQgPT0gIkNPMiIpICU+JSANCiAgZmlsdGVyKGVtaXNzaW9uX3llYXIgPT0gIjIwMDUiKQ0KYGBgDQoNCmBgYHtyfQ0KdG90YWxfZW1pc3Npb25zX2Zvcl9nYXMgPC0gZmlsdGVyZWRfZGYgJT4lIA0KICBzdW1tYXJpc2Uoc3VtKGVtaXNzaW9ucykpICU+JSANCiAgcHVsbCgpDQoNCnRvdGFsX2VtaXNzaW9uc19ieV9jYXRlZ29yeSA8LSBmaWx0ZXJlZF9kZiAlPiUgDQogIHNlbGVjdCgtcG9sbHV0YW50KSAlPiUgDQogIGdyb3VwX2J5KGNjcF9tYXBwaW5nKSAlPiUgDQogIHN1bW1hcmlzZShjYXRfc3VtID0gc3VtKGVtaXNzaW9ucyksIC5ncm91cHMgPSAnZHJvcF9sYXN0JykNCmBgYA0KDQpgYGB7cn0NCmNhdGVnb3JpZXMgPC0gZmlsdGVyZWRfZGYgJT4lIA0KICBkaXN0aW5jdChjY3BfbWFwcGluZykgJT4lIA0KICBwdWxsKCkNCg0Kbl9jYXRlZ29yaWVzIDwtIGxlbmd0aChjYXRlZ29yaWVzKQ0KDQpzb3VyY2VzIDwtIGZpbHRlcmVkX2RmICU+JSANCiAgZGlzdGluY3Qoc291cmNlX25hbWUpICU+JSANCiAgcHVsbCgpDQoNCm5fc291cmNlcyA8LSBsZW5ndGgoc291cmNlcykNCmBgYA0KDQpgYGB7cn0NCm5fc291cmNlcw0KYGBgDQoNCg0KYGBge3J9DQpub2RlX25hbWVzIDwtIGMoIlRvdGFsIiwgY2F0ZWdvcmllcywgc291cmNlcywgIk90aGVyIikNCg0Kbm9kZV9uYW1lc19kZiA8LSBkYXRhLmZyYW1lKCJuYW1lIiA9IG5vZGVfbmFtZXMpDQoNCnRvdGFsX3NhbmtleV90aWJibGUgPC0gdG90YWxfZW1pc3Npb25zX2J5X2NhdGVnb3J5ICU+JQ0KICBtdXRhdGUodG90YWwgPSAiVG90YWwiKSAlPiUgDQogIG11dGF0ZSh0b3RhbCA9IG1hdGNoKHRvdGFsLCBub2RlX25hbWVzKSAtMSkgJT4lIA0KICBtdXRhdGUoY2NwX21hcHBpbmcgPSBtYXRjaChjY3BfbWFwcGluZywgbm9kZV9uYW1lcykgLTEpICU+JSANCiAgc2VsZWN0KHNvdXJjZSA9IHRvdGFsLA0KICAgICAgICAgdGFyZ2V0ID0gY2NwX21hcHBpbmcsDQogICAgICAgICB2YWx1ZSA9IGNhdF9zdW0pDQoNCnRvdGFsX2ZpbHRlcmVkX2VtaXNzaW9ucyA8LSB0b3RhbF9zYW5rZXlfdGliYmxlICU+JSANCiAgc3VtbWFyaXNlKHN1bSh2YWx1ZSkpICU+JSANCiAgcHVsbCgpDQoNCm90aGVyX2VtaXNzaW9ucyA8LSB0b3RhbF9lbWlzc2lvbnNfZm9yX2dhcyAtIHRvdGFsX2ZpbHRlcmVkX2VtaXNzaW9ucw0KDQp0b3RhbF9vdGhlcl9zYW5rZXlfdGliYmxlIDwtIHRpYmJsZSgNCiAgInNvdXJjZSIgPSBjKDApLA0KICAidGFyZ2V0IiA9IChtYXRjaCgiT3RoZXIiLCBub2RlX25hbWVzKSAtMSksDQogICJ2YWx1ZSIgPSBjKG90aGVyX2VtaXNzaW9ucykNCikNCg0KDQpzdWJfc2Fua2V5X3RpYmJsZSA8LSBmaWx0ZXJlZF9kZiAlPiUgDQogIHNlbGVjdCgtIGModW5pdHMsIHBvbGx1dGFudCwgZW1pc3Npb25feWVhcikpICU+JSANCiAgbXV0YXRlKGNjcF9tYXBwaW5nID0gbWF0Y2goY2NwX21hcHBpbmcsIG5vZGVfbmFtZXMpIC0xLA0KICAgICAgICAgc291cmNlX25hbWUgPSBtYXRjaChzb3VyY2VfbmFtZSwgbm9kZV9uYW1lcykgLTEpDQoNCm5hbWVzKHN1Yl9zYW5rZXlfdGliYmxlKSA9IGMoInNvdXJjZSIsICJ0YXJnZXQiLCAidmFsdWUiKQ0KDQpzYW5rZXlfdGliYmxlIDwtIHRvdGFsX3NhbmtleV90aWJibGUgJT4lIA0KICBiaW5kX3Jvd3Moc3ViX3NhbmtleV90aWJibGUpICU+JSANCiAgYmluZF9yb3dzKHRvdGFsX290aGVyX3NhbmtleV90aWJibGUpDQoNCmxpbmtzX21hdHJpeCA8LSBkYXRhLmZyYW1lKGFzLm1hdHJpeChzYW5rZXlfdGliYmxlLCBieXJvdyA9IFRSVUUsIG5jb2xzID0gMykpDQoNCiMgQWRkIGEgJ2dyb3VwJyBjb2x1bW4gdG8gZWFjaCBjb25uZWN0aW9uOg0KbGlua3MgPC0gbGlua3NfbWF0cml4ICU+JSANCiAgbXV0YXRlKGdyb3VwID0gY2FzZV93aGVuKA0KICAgIHNvdXJjZSA9PSAwIH4gcGFzdGUoInR5cGVfIiwgdGFyZ2V0LCBzZXAgPSAiIiksDQogICAgc291cmNlIT0wIH4gcGFzdGUoInR5cGVfIiwgc291cmNlLCBzZXAgPSAiIikNCiAgKSkNCg0Kbm9kZXMgPC0gbm9kZV9uYW1lc19kZg0KIyBBZGQgYSAnZ3JvdXAnIGNvbHVtbiB0byBlYWNoIG5vZGUuDQojIEFsbCBvZiB0aGVtIGluIHRoZSBzYW1lIGdyb3VwIHRvIG1ha2UgdGhlbSB0aGUgc2FtZSBjb2xvdXINCm5vZGVzJGdyb3VwIDwtIGFzLmZhY3RvcihjKCJteV91bmlxdWVfZ3JvdXAiKSkNCg0KZW1pc3Npb25zIDwtIGxpc3QoKQ0KDQplbWlzc2lvbnMkbm9kZXMgPC0gbm9kZXMNCmVtaXNzaW9ucyRsaW5rcyA8LSBsaW5rcw0KDQoNCmBgYA0KDQoNCg0KDQoNCg0KYGBge3J9DQp0b3RhbF9maWx0ZXJlZF9lbWlzc2lvbnMgPC0gdG90YWxfc2Fua2V5X3RpYmJsZSAlPiUgDQogIHN1bW1hcmlzZShzdW0odmFsdWUpKSAlPiUgDQogIHB1bGwoKQ0KDQpvdGhlcl9lbWlzc2lvbnMgPC0gdG90YWxfZW1pc3Npb25zX2Zvcl9nYXMgLSB0b3RhbF9maWx0ZXJlZF9lbWlzc2lvbnMNCg0KdG90YWxfb3RoZXJfc2Fua2V5X3RpYmJsZSA8LSB0aWJibGUoDQogICJzb3VyY2UiID0gYygwKSwNCiAgInRhcmdldCIgPSAobWF0Y2goIk90aGVyIiwgbm9kZV9uYW1lcykgLTEpLA0KICAidmFsdWUiID0gYyhvdGhlcl9lbWlzc2lvbnMpDQopDQoNCnN1Yl9zYW5rZXlfdGliYmxlIDwtIGZpbHRlcmVkX3RpYmJsZSAlPiUgDQogIHNlbGVjdCgtY2F0ZWdvcnlfaWQsIC1zdWJjYXRlZ29yeV9pZCwgLXllYXIpICU+JSANCiAgbXV0YXRlKGNhdGVnb3J5X25hbWUgPSBtYXRjaChjYXRlZ29yeV9uYW1lLCBub2RlX25hbWVzKSAtMSwNCiAgICAgICAgIHN1YmNhdGVnb3J5X25hbWUgPSBtYXRjaChzdWJjYXRlZ29yeV9uYW1lLCBub2RlX25hbWVzKSAtMSkNCg0KbmFtZXMoc3ViX3NhbmtleV90aWJibGUpID0gYygic291cmNlIiwgInRhcmdldCIsICJ2YWx1ZSIpDQoNCnNhbmtleV90aWJibGUgPC0gdG90YWxfc2Fua2V5X3RpYmJsZSAlPiUgDQogIGJpbmRfcm93cyhzdWJfc2Fua2V5X3RpYmJsZSkgJT4lIA0KICBiaW5kX3Jvd3ModG90YWxfb3RoZXJfc2Fua2V5X3RpYmJsZSkNCg0KbGlua3NfbWF0cml4IDwtIGRhdGEuZnJhbWUoYXMubWF0cml4KHNhbmtleV90aWJibGUsIGJ5cm93ID0gVFJVRSwgbmNvbHMgPSAzKSkNCg0KIyBBZGQgYSAnZ3JvdXAnIGNvbHVtbiB0byBlYWNoIGNvbm5lY3Rpb246DQpsaW5rcyA8LSBsaW5rc19tYXRyaXggJT4lIA0KICBtdXRhdGUoZ3JvdXAgPSBjYXNlX3doZW4oDQogICAgc291cmNlID09IDAgfiBwYXN0ZSgidHlwZV8iLCB0YXJnZXQsIHNlcCA9ICIiKSwNCiAgICBzb3VyY2UhPTAgfiBwYXN0ZSgidHlwZV8iLCBzb3VyY2UsIHNlcCA9ICIiKQ0KICApKQ0KDQpub2RlcyA8LSBub2RlX25hbWVzX2RmDQojIEFkZCBhICdncm91cCcgY29sdW1uIHRvIGVhY2ggbm9kZS4NCiMgQWxsIG9mIHRoZW0gaW4gdGhlIHNhbWUgZ3JvdXAgdG8gbWFrZSB0aGVtIHRoZSBzYW1lIGNvbG91cg0Kbm9kZXMkZ3JvdXAgPC0gYXMuZmFjdG9yKGMoIm15X3VuaXF1ZV9ncm91cCIpKQ0KDQplbWlzc2lvbnMgPC0gbGlzdCgpDQoNCmVtaXNzaW9ucyRub2RlcyA8LSBub2Rlcw0KZW1pc3Npb25zJGxpbmtzIDwtIGxpbmtzDQpgYGANCg0KDQoNCg0KDQoNCg0KDQpgYGB7cn0NCm1ha2Vfc2Fua2V5X2RmcyA8LSBmdW5jdGlvbihkYXRhLCB1c2VyWWVhciwgdXNlckdhcykgew0KICBuX2NhdGVnb3JpZXMgPC0gZGF0YSAlPiUgDQogICAgZGlzdGluY3QoY2F0ZWdvcnlfbmFtZSkgJT4lIA0KICAgIG5yb3coKQ0KICANCiAgdG90YWxfZW1pc3Npb25zX2Zvcl9nYXMgPC0gZGF0YSAlPiUgDQogICAgZmlsdGVyKGVtaXNzaW9uID09IHVzZXJHYXMoKSkgJT4lIA0KICAgIGZpbHRlcih5ZWFyID09IHVzZXJZZWFyKCkpICU+JQ0KICAgIHN1bW1hcmlzZShzdW0oZW1pc3Npb25zKSkgJT4lIA0KICAgIHB1bGwoKQ0KICANCiAgZmlsdGVyZWRfdGliYmxlIDwtIGRhdGEgJT4lDQogICAgZmlsdGVyKGVtaXNzaW9uID09IHVzZXJHYXMoKSkgJT4lIA0KICAgIHNlbGVjdCgtZW1pc3Npb24pICU+JSANCiAgICBmaWx0ZXIoeWVhciA9PSB1c2VyWWVhcigpKSAlPiUgDQogICAgZmlsdGVyKGVtaXNzaW9ucyA+IHVzZXJSZXNvbHV0aW9uKCkpDQogIA0KICB0b3RhbF9lbWlzc2lvbnNfYnlfY2F0IDwtIGZpbHRlcmVkX3RpYmJsZSAlPiUNCiAgICBncm91cF9ieShjYXRlZ29yeV9uYW1lKSAlPiUgDQogICAgc3VtbWFyaXNlKGNhdF9zdW0gPSBzdW0oZW1pc3Npb25zKSwgLmdyb3VwcyA9ICdkcm9wX2xhc3QnKQ0KICANCiAgY2F0ZWdvcmllcyA8LSBmaWx0ZXJlZF90aWJibGUgJT4lDQogICAgZGlzdGluY3QoY2F0ZWdvcnlfbmFtZSkgJT4lIA0KICAgIHB1bGwoKQ0KICANCiAgc3ViY2F0ZWdvcmllcyA8LSBmaWx0ZXJlZF90aWJibGUgJT4lDQogICAgZGlzdGluY3Qoc3ViY2F0ZWdvcnlfbmFtZSkgJT4lIA0KICAgIHB1bGwoKQ0KICANCiAgbm9kZV9uYW1lcyA8LSBjKCJUb3RhbCIsIGNhdGVnb3JpZXMsIHN1YmNhdGVnb3JpZXMsICJPdGhlciIpDQogIA0KICBub2RlX25hbWVzX2RmIDwtIGRhdGEuZnJhbWUoIm5hbWUiID0gbm9kZV9uYW1lcykNCiAgDQogIHRvdGFsX3NhbmtleV90aWJibGUgPC0gdG90YWxfZW1pc3Npb25zX2J5X2NhdCAlPiUNCiAgICBtdXRhdGUodG90YWwgPSAiVG90YWwiKSAlPiUgDQogICAgbXV0YXRlKHRvdGFsID0gbWF0Y2godG90YWwsIG5vZGVfbmFtZXMpIC0xKSAlPiUgDQogICAgbXV0YXRlKGNhdGVnb3J5X25hbWUgPSBtYXRjaChjYXRlZ29yeV9uYW1lLCBub2RlX25hbWVzKSAtMSkgJT4lIA0KICAgIHNlbGVjdChzb3VyY2UgPSB0b3RhbCwNCiAgICAgICAgICAgdGFyZ2V0ID0gY2F0ZWdvcnlfbmFtZSwNCiAgICAgICAgICAgdmFsdWUgPSBjYXRfc3VtKQ0KICANCiAgdG90YWxfZmlsdGVyZWRfZW1pc3Npb25zIDwtIHRvdGFsX3NhbmtleV90aWJibGUgJT4lIA0KICAgIHN1bW1hcmlzZShzdW0odmFsdWUpKSAlPiUgDQogICAgcHVsbCgpDQogIA0KICBvdGhlcl9lbWlzc2lvbnMgPC0gdG90YWxfZW1pc3Npb25zX2Zvcl9nYXMgLSB0b3RhbF9maWx0ZXJlZF9lbWlzc2lvbnMNCiAgDQogIHRvdGFsX290aGVyX3NhbmtleV90aWJibGUgPC0gdGliYmxlKA0KICAgICJzb3VyY2UiID0gYygwKSwNCiAgICAidGFyZ2V0IiA9IChtYXRjaCgiT3RoZXIiLCBub2RlX25hbWVzKSAtMSksDQogICAgInZhbHVlIiA9IGMob3RoZXJfZW1pc3Npb25zKQ0KICApDQogIA0KICBzdWJfc2Fua2V5X3RpYmJsZSA8LSBmaWx0ZXJlZF90aWJibGUgJT4lIA0KICAgIHNlbGVjdCgtY2F0ZWdvcnlfaWQsIC1zdWJjYXRlZ29yeV9pZCwgLXllYXIpICU+JSANCiAgICBtdXRhdGUoY2F0ZWdvcnlfbmFtZSA9IG1hdGNoKGNhdGVnb3J5X25hbWUsIG5vZGVfbmFtZXMpIC0xLA0KICAgICAgICAgICBzdWJjYXRlZ29yeV9uYW1lID0gbWF0Y2goc3ViY2F0ZWdvcnlfbmFtZSwgbm9kZV9uYW1lcykgLTEpDQogIA0KICBuYW1lcyhzdWJfc2Fua2V5X3RpYmJsZSkgPSBjKCJzb3VyY2UiLCAidGFyZ2V0IiwgInZhbHVlIikNCiAgDQogIHNhbmtleV90aWJibGUgPC0gdG90YWxfc2Fua2V5X3RpYmJsZSAlPiUgDQogICAgYmluZF9yb3dzKHN1Yl9zYW5rZXlfdGliYmxlKSAlPiUgDQogICAgYmluZF9yb3dzKHRvdGFsX290aGVyX3NhbmtleV90aWJibGUpDQogIA0KICBsaW5rc19tYXRyaXggPC0gZGF0YS5mcmFtZShhcy5tYXRyaXgoc2Fua2V5X3RpYmJsZSwgYnlyb3cgPSBUUlVFLCBuY29scyA9IDMpKQ0KICANCiAgIyBBZGQgYSAnZ3JvdXAnIGNvbHVtbiB0byBlYWNoIGNvbm5lY3Rpb246DQogIGxpbmtzIDwtIGxpbmtzX21hdHJpeCAlPiUgDQogICAgbXV0YXRlKGdyb3VwID0gY2FzZV93aGVuKA0KICAgICAgc291cmNlID09IDAgfiBwYXN0ZSgidHlwZV8iLCB0YXJnZXQsIHNlcCA9ICIiKSwNCiAgICAgIHNvdXJjZSE9MCB+IHBhc3RlKCJ0eXBlXyIsIHNvdXJjZSwgc2VwID0gIiIpDQogICAgKSkNCiAgDQogIG5vZGVzIDwtIG5vZGVfbmFtZXNfZGYNCiAgIyBBZGQgYSAnZ3JvdXAnIGNvbHVtbiB0byBlYWNoIG5vZGUuDQogICMgQWxsIG9mIHRoZW0gaW4gdGhlIHNhbWUgZ3JvdXAgdG8gbWFrZSB0aGVtIHRoZSBzYW1lIGNvbG91cg0KICBub2RlcyRncm91cCA8LSBhcy5mYWN0b3IoYygibXlfdW5pcXVlX2dyb3VwIikpDQogIA0KICBlbWlzc2lvbnMgPC0gbGlzdCgpDQogIA0KICBlbWlzc2lvbnMkbm9kZXMgPC0gbm9kZXMNCiAgZW1pc3Npb25zJGxpbmtzIDwtIGxpbmtzDQogIA0KICByZXR1cm4oZW1pc3Npb25zKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQptYWtlX3NhbmtleV9kZnMoZW1pc3Npb25zX3NhbmtleSwgdXNlclllYXIgPSAyMDA1LCB1c2VyR2FzID0gIkNINCIsIHVzZXJSZXNvbHV0aW9uID0gNTApDQpgYGANCmBgYHtyfQ0KDQpgYGANCg0K